package com.example.bigupload.service;

import cn.hutool.core.lang.Assert;
import com.example.bigupload.domain.AjaxResult;
import com.example.bigupload.domain.FileChunk;
import com.example.bigupload.domain.FileInfo;
import com.example.bigupload.mapper.FileChunkMapper;
import com.example.bigupload.mapper.FileInfoMapper;
import com.example.bigupload.utils.FileTypeUtils;
import io.minio.ComposeObjectArgs;
import io.minio.ComposeSource;
import io.minio.ObjectWriteResponse;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author HTT
 */
@Slf4j
@Service
public class UploadService {

    /**
     * 默认的分片大小：50MB
     */
    public static final long DEFAULT_CHUNK_SIZE = 50 * 1024 * 1024;

    @Resource
    private FileChunkMapper fileChunkMapper;

    @Resource
    private FileInfoMapper fileInfoMapper;

    @Resource
    private MinioService minioService;




    public AjaxResult checkUpload(FileChunk fileChunk) {
        //通过MD5查询文件的保存的分片数与前端要传进来的分片数是否一致
        List<FileChunk> list = fileChunkMapper.findFileChunkParamByMd5(fileChunk.getIdentifier());
        Map<String, Object> data = new HashMap<>(1);
        // 判断文件存不存在
        if (list == null || list.isEmpty()) {
            data.put("uploaded", false);
            return AjaxResult.success("文件上传成功",data);
        }
        // 处理单文件
        if (list.get(0).getTotalChunks() == 1) {
            data.put("uploaded", true);
            data.put("url", "");
            return AjaxResult.success("文件上传成功",data);
        }
        // 处理分片
        int[] uploadedFiles = new int[list.size()];
        int index = 0;
        for (FileChunk fileChunkItem : list) {
            uploadedFiles[index] = fileChunkItem.getChunkNumber();
            index++;
        }
        data.put("uploadedChunks", uploadedFiles);
        return AjaxResult.success("文件上传成功",data);
    }

    /**
     * 上传分片文件
     * @param fileChunk
     * @return
     * @throws Exception
     */
    public AjaxResult uploadChunkFile(FileChunk fileChunk) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {

        String filename = fileChunk.getFilename();
        String[] split = filename.split("");
        int length = split.length;


        String fileOssPatch= minioService.putObject(fileChunk.getFile(), "filetest", "zip");
        Assert.isTrue(fileOssPatch!=null,"保存到Oss文件分片信息失败");
        fileChunk.setOssPath(fileOssPatch);


        fileChunk.setCreateTime(new Date());
        fileChunkMapper.insertFileChunk(fileChunk);
        //数据库中已上传的分片总数,和前端传递的分片总数一致
        Integer count = fileChunkMapper.findCountByMd5(fileChunk.getIdentifier());

        log.info("上传的 数据库分片总数 {} 前端的分片总数 {}",count,fileChunk.getTotalChunks());
        if(fileChunk.getTotalChunks().equals(count)){
            String fileName = fileChunk.getFilename();
            //1.数据库中已上传的分片总数,和前端传递的分片总数一致,直接开始合并

            //查出MD5一
            // 要合并的分片文件信息
            String objectName = UUID.randomUUID().toString().replaceAll("-", "") + fileName.substring(fileName.lastIndexOf("."));
            //合并分片文件
            minioService.mergeFiles(fileChunk.getIdentifier(), objectName);

            //2.保存合并后的文件信息
            FileInfo fileInfo = new FileInfo();
            String originalFilename = fileChunk.getFile().getOriginalFilename();
            fileInfo.setId(UUID.randomUUID().toString());
            fileInfo.setOriginFileName(originalFilename);
            fileInfo.setFileName(fileName);
            fileInfo.setFilePath(objectName);
            fileInfo.setFileSize(fileChunk.getTotalSize());
            fileInfo.setCreateTime(new Date());
            fileInfoMapper.insert(fileInfo);
            //3.删除分片数据信息
            List<FileChunk> fileChunkParamByMd5 = fileChunkMapper.findFileChunkParamByMd5(fileChunk.getIdentifier());
            List<String> objectNameList = fileChunkParamByMd5.stream().map(FileChunk::getOssPath).collect(Collectors.toList());
            minioService.removeListObject("filetest", objectNameList);
        }


        return AjaxResult.success("文件上传成功");

    }

    private void uploadFileByRandomAccessFile(String filePath, FileChunk fileChunk) throws IOException {

        RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");
        // 分片大小必须和前端匹配，否则上传会导致文件损坏
        long chunkSize = fileChunk.getChunkSize() == 0L ? DEFAULT_CHUNK_SIZE : fileChunk.getChunkSize().longValue();
        // 偏移量
        long offset = chunkSize * (fileChunk.getChunkNumber() - 1);
        // 定位到该分片的偏移量
        randomAccessFile.seek(offset);
        // 写入
        randomAccessFile.write(fileChunk.getFile().getBytes());
        randomAccessFile.close();

    }


}
